// src/main/ets/utils/FileManager.ts
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
import resourceManager from '@ohos.resourceManager';
import hilog from '@ohos.hilog';

const TAG = 'FileManager';
const DOMAIN = 0x0000;

export class FileUtils {
  /**
   * 从rawfile读取文件内容
   * @param context 应用上下文
   * @param fileName 文件名
   * @returns 文件内容字符串
   */
  public static async readRawFile(context: common.UIAbilityContext, fileName: string): Promise<string> {
    try {
      const resourceManager = context.resourceManager;
      const arrayBuffer = await resourceManager.getRawFileContent(fileName);
      // 将ArrayBuffer转换为字符串
      return String.fromCharCode.apply(null, new Uint8Array(arrayBuffer));
    } catch (error) {
      console.error(`Failed to read rawfile ${fileName}:`, error);
      throw error;
    }
  }

  /**
   * 将 rawfile 文件拷贝至沙箱目录
   * @param context 应用上下文
   * @param rawFileName rawfile 中的文件名
   * @param targetDir 目标目录（可选，默认使用 filesDir）
   * @returns 拷贝后的完整路径
   */
  public static async copyRawFileToSandbox(
    context: common.Context,
    rawFileName: string,
    targetDir?: string
  ): Promise<string> {
    try {
      // 1. 确定目标目录
      const sandboxDir = targetDir || context.filesDir;
      hilog.info(DOMAIN, TAG, `context.filesDir: ${context.filesDir}`);
      const targetPath = `${sandboxDir}/${rawFileName}`;

      // 2. 检查文件是否已存在
      if (fs.accessSync(targetPath)) {
        hilog.info(DOMAIN, TAG, `File already exists: ${targetPath}`);
        return targetPath;
      }

      // 3. 读取 rawfile 内容
      const rawFileData = await context.resourceManager.getRawFileContent(rawFileName);

      // 4. 写入沙箱目录
      const file = fs.openSync(targetPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
      fs.writeSync(file.fd, rawFileData.buffer);
      fs.closeSync(file.fd);

      hilog.info(DOMAIN, TAG, `Copied to: ${targetPath}`);
      return targetPath;
    } catch (error) {
      hilog.error(DOMAIN, TAG, `Copy failed: ${JSON.stringify(error)}`);
      throw new Error(`Failed to copy ${rawFileName}: ${error.message}`);
    }
  }

  /**
   * 批量拷贝 rawfile 文件
   * @param context 应用上下文
   * @param fileNames 文件名数组
   * @param targetDir 目标目录（可选）
   */
  public static async batchCopyRawFiles(
    context: common.Context,
    fileNames: string[],
    targetDir?: string
  ): Promise<void> {
    await Promise.all(
      fileNames.map(fileName => this.copyRawFileToSandbox(context, fileName, targetDir))
    );
  }

  /**
   * 从filesDir读取文件内容
   * @param context 应用上下文
   * @param fileName 文件名
   * @returns 文件内容字符串
   */
  public static async readFromFilesDir(
    context: common.UIAbilityContext,
    fileName: string
  ): Promise<string> {
    const filePath = `${context.filesDir}/${fileName}`;
    let file: fs.File | null = null;

    try {
      // 1. 检查文件是否存在
      await fs.access(filePath);
      hilog.info(DOMAIN, TAG, `File found: ${filePath}`);

      // 2. 打开文件
      file = await fs.open(filePath, fs.OpenMode.READ_ONLY);
      hilog.info(DOMAIN, TAG, `File opened: ${filePath}`);

      // 3. 获取文件信息
      const stat = await fs.stat(filePath);
      hilog.info(DOMAIN, TAG, `File size: ${stat.size} bytes`);

      // 4. 读取文件内容
      const buffer = new ArrayBuffer(stat.size);
      await fs.read(file.fd, buffer);

      // 5. 将ArrayBuffer转换为字符串
      const uint8Array = new Uint8Array(buffer);
      let content = '';
      for (let i = 0; i < uint8Array.length; i++) {
        content += String.fromCharCode(uint8Array[i]);
      }

      return content;
    } catch (error) {
      hilog.error(DOMAIN, TAG, `Failed to read file ${filePath}: ${JSON.stringify(error)}`);
      throw new Error(`Failed to read file: ${error.message}`);
    } finally {
      // 6. 确保关闭文件
      if (file) {
        try {
          await fs.close(file.fd);
          hilog.info(DOMAIN, TAG, `File closed: ${filePath}`);
        } catch (closeError) {
          hilog.error(DOMAIN, TAG, `Failed to close file: ${closeError}`);
        }
      }
    }
  }

  /**
   * 从沙箱目录读取文件内容
   * @param context 应用上下文
   * @param filePath 文件路径（相对或绝对）
   * @returns 文件内容字符串
   */
  public static async readFromSandbox(
    context: common.UIAbilityContext,
    filePath: string
  ): Promise<string> {
    // 处理相对路径
    const fullPath = filePath.startsWith('/') ?
      filePath : `${context.filesDir}/${filePath}`;

    let file: fs.File | null = null;

    try {
      // 1. 检查文件是否存在
      await fs.access(fullPath);
      hilog.info(DOMAIN, TAG, `找到文件: ${fullPath}`);

      // 2. 打开文件
      file = await fs.open(fullPath, fs.OpenMode.READ_ONLY);
      hilog.info(DOMAIN, TAG, `打开文件: ${fullPath}`);

      // 3. 获取文件大小
      const stat = await fs.stat(fullPath);
      hilog.info(DOMAIN, TAG, `读取文件大小: ${fullPath}`);

      // 4. 读取文件内容
      const buffer = new ArrayBuffer(stat.size);
      await fs.read(file.fd, buffer);
      hilog.info(DOMAIN, TAG, `读取文件: ${fullPath}`);

      // 5. 转换为字符串
      const uint8Array = new Uint8Array(buffer);
      let content = '';
      for (let i = 0; i < uint8Array.length; i++) {
        content += String.fromCharCode(uint8Array[i]);
      }
      hilog.info(DOMAIN, TAG, `读取内容: ${fullPath}, context is %{public}s.`, content);

      return content;
    } catch (error) {
      hilog.error(DOMAIN, TAG, `read file err ${fullPath}, %{public}s`, error);
      throw error;
    } finally {
      // 6. 确保关闭文件
      if (file) {
        await fs.close(file.fd).catch(closeError => {
          hilog.error(DOMAIN, TAG, 'colse file err %{public}s:', closeError);
        });
      }
    }
  }

  /**
   * 读取并打印文件内容
   * @param context 应用上下文
   * @param fileName 文件名
   */
  public static async readAndPrintFile(
    context: common.UIAbilityContext,
    fileName: string
  ): Promise<void> {
    try {
      const content = await this.readFromFilesDir(context, fileName);
      hilog.info(DOMAIN, TAG, `=== Content of ${fileName} ===`);
      hilog.info(DOMAIN, TAG, content);
      hilog.info(DOMAIN, TAG, `=== End of ${fileName} ===`);
    } catch (error) {
      hilog.info(DOMAIN, TAG, `Failed to print file ${fileName}, err is %{public)s`, error);
      throw error;
    }
  }
}